#include "config.h"

#include <Windows.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <stdlib.h>
#include "aux_types.h"
#include "aux_errno.h"
#include "win_thread.h"
#include "aux_time.h"
#include "aux_debugmsg.h"

u64 _pthread_time_in_ms(void)
{
    struct timeb tb;

    ftime(&tb);

    return tb.time * 1000 + tb.millitm;
}

u64 _pthread_time_in_ms_from_timespec(const struct timespec *ts)
{
    if (ts)
        return ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
    else
        return 0;
}

u64 _pthread_rel_time_in_ms(const struct timespec *ts)
{
    u64 t1 = _pthread_time_in_ms_from_timespec(ts);
    u64 t2 = _pthread_time_in_ms();

    /* Prevent underflow */
    if (t1 < t2)
        return 0;
    else
        return t1 - t2;
}

int pthread_mutex_init(pthread_mutex_t *ptMutex,
                               const pthread_mutexattr_t *attr)
{
    HANDLE h;

    if (!ptMutex)
        return -EFAULT;

    if (MUTEX_STATE_UNINIT != ptMutex->eState)
        return -EBUSY;

    h = CreateMutex(NULL, FALSE, NULL);

    if (NULL == h)
        return -EAGAIN;

    ptMutex->eState = MUTEX_STATE_UNLOCK;
    return 0;
}

int pthread_mutex_lock(pthread_mutex_t *ptMutex)
{
    DWORD dwRet;

    if ((!ptMutex) || (MUTEX_STATE_UNINIT == ptMutex->eState))
        return -EINVAL;

    dwRet = WaitForSingleObject(ptMutex->hMutex, INFINITE);
    if (WAIT_OBJECT_0 == dwRet)
    {
        ptMutex->eState = MUTEX_STATE_LOCK;
        return 0;
    }

    if (WAIT_ABANDONED == dwRet)
    {
        ptMutex->eState = MUTEX_STATE_UNREACHABLE;
        return -ENOTRECOVERABLE;
    }

    if (WAIT_FAILED == dwRet)
    {
        ptMutex->eState = MUTEX_STATE_UNLOCK;
        return -EAGAIN;
    }

    return -EAGAIN;
}

int pthread_mutex_unlock(pthread_mutex_t *ptMutex)
{
    BOOL bRet;

    if (!ptMutex)
        return -EINVAL;

    if (MUTEX_STATE_LOCK != ptMutex->eState)
        return -EPERM;

    bRet = ReleaseMutex(ptMutex->hMutex);
    if (TRUE != bRet)
        return -EAGAIN;

    ptMutex->eState = MUTEX_STATE_UNLOCK;
    return 0;
}

int pthread_mutex_destroy(pthread_mutex_t *ptMutex)
{
    BOOL bRet;

    if (!ptMutex)
        return -EINVAL;

    if (MUTEX_STATE_LOCK == ptMutex->eState)
        return -EBUSY;
    if (MUTEX_STATE_UNINIT == ptMutex->eState)
        return 0;

    bRet = CloseHandle(ptMutex->hMutex);
    if (TRUE != bRet)
        return -EAGAIN;

    ptMutex->eState = MUTEX_STATE_UNINIT;
    ptMutex->hMutex = NULL;
    return 0;
}


int pthread_cond_init(pthread_cond_t *ptCond, const pthread_condattr_t *s)
{
    if (!ptCond)
        return -ERRNO_EPARAMETER_INVALID;

    ptCond->nCntOfThreadWaiting = 0;
    ptCond->bIsBroadcasting     = 0;
    ptCond->hSemephore          = CreateSemaphore(NULL,       // no security
                                                  0,          // initially 0
                                                  0x7fffffff, // max count
                                                  NULL);      // unnamed
    if (NULL == ptCond->hSemephore)
        return GetLastError();

    InitializeCriticalSection(&ptCond->lockForCntOfWaiting);
    ptCond->hEventAllWakeUpDone = CreateEvent(NULL,  // no security
                                              FALSE, // auto-reset
                                              FALSE, // non-signaled initially
                                              NULL); // unnamed
    return (NULL == ptCond->hEventAllWakeUpDone) ? GetLastError() : 0;
}

int pthread_cond_destroy(pthread_cond_t *ptCond)
{
    if (!ptCond)
        return -ERRNO_EPARAMETER_INVALID;

    CloseHandle(ptCond->hSemephore);
    DeleteCriticalSection(&ptCond->lockForCntOfWaiting);
    return (CloseHandle(ptCond->hEventAllWakeUpDone)) ? 0 : GetLastError();
}

int pthread_cond_signal(pthread_cond_t *ptCond)
{
    u8 bWaitorExist = 0;

    if (!ptCond)
        return -ERRNO_EPARAMETER_INVALID;

    EnterCriticalSection(&(ptCond->lockForCntOfWaiting));
    bWaitorExist = ptCond->nCntOfThreadWaiting > 0 ? 1 : 0;
    LeaveCriticalSection(&ptCond->lockForCntOfWaiting);

    if (bWaitorExist)
        return (ReleaseSemaphore(ptCond->hSemephore, 1, 0)) ? 0:GetLastError();
    else
        return 0; /* no waitor, no operation */
}

int pthread_cond_broadcast(pthread_cond_t *ptCond)
{
    if (!ptCond)
        return -ERRNO_EPARAMETER_INVALID;

    EnterCriticalSection(&ptCond->lockForCntOfWaiting);

    if (ptCond->nCntOfThreadWaiting > 0)
    {
        ptCond->bIsBroadcasting = 1;
        ReleaseSemaphore(ptCond->hSemephore, ptCond->nCntOfThreadWaiting, 0);
        LeaveCriticalSection(&ptCond->lockForCntOfWaiting);

        WaitForSingleObject(ptCond->hEventAllWakeUpDone, INFINITE);
        ptCond->bIsBroadcasting = 0;
    }
    else
        LeaveCriticalSection(&ptCond->lockForCntOfWaiting);

    return 0;
}

int pthread_cond_wait(pthread_cond_t *ptCond, pthread_mutex_t *ptExMutex)
{
    u8 byLastWaitorFound = 0;

    if ((!ptCond) || (!ptExMutex) || (MUTEX_STATE_LOCK != ptExMutex->eState))
        -ERRNO_EPARAMETER_INVALID;

    EnterCriticalSection(&(ptCond->lockForCntOfWaiting));
    ptCond->nCntOfThreadWaiting++;
    LeaveCriticalSection(&(ptCond->lockForCntOfWaiting));

    ptExMutex->eState = MUTEX_STATE_UNLOCK;
    SignalObjectAndWait(ptExMutex->hMutex, ptCond->hSemephore, INFINITE, FALSE);

    EnterCriticalSection(&ptCond->lockForCntOfWaiting);
    ptCond->nCntOfThreadWaiting--;
    byLastWaitorFound = (ptCond->bIsBroadcasting) &&
                        (0 == ptCond->nCntOfThreadWaiting);
    LeaveCriticalSection(&ptCond->lockForCntOfWaiting);

    if (byLastWaitorFound)
        SignalObjectAndWait(ptCond->hEventAllWakeUpDone,
                            ptExMutex->hMutex, INFINITE, FALSE);
    else
        WaitForSingleObject(ptExMutex->hMutex, INFINITE);
    ptExMutex->eState = MUTEX_STATE_LOCK;

    return 0;
}

int pthread_cond_timedwait(pthread_cond_t *ptCond,
                                   pthread_mutex_t *ptExMutex,
                                   const struct timespec *abstime)
{
    u8 byLastWaitorFound = 0;
    DWORD nRet;
    u64 waitMs;
    u64 curMs;
    u64 endMs;

    if ((!ptCond) || (!ptExMutex) || (!abstime) || (MUTEX_STATE_LOCK != ptExMutex->eState))
        return -ERRNO_EPARAMETER_INVALID;

    if (ERRNO_SUCCESS != aux_get_timestamp_ms(&curMs))
        return -ERRNO_EGETTIMEERROR;

    endMs = abstime->tv_sec * 1000 + abstime->tv_nsec / 1000;

    if (endMs <= curMs)
        return -ETIMEDOUT;
    waitMs = endMs - curMs;
    if (waitMs > 1000)
        aux_prt_debug("wait %llu ms\n", waitMs);

    EnterCriticalSection(&(ptCond->lockForCntOfWaiting));
    ptCond->nCntOfThreadWaiting++;
    LeaveCriticalSection(&(ptCond->lockForCntOfWaiting));

    ptExMutex->eState = MUTEX_STATE_UNLOCK;
    nRet = SignalObjectAndWait(ptExMutex->hMutex,
                               ptCond->hSemephore,
                               (u32)waitMs,
                               FALSE);

    EnterCriticalSection(&ptCond->lockForCntOfWaiting);
    ptCond->nCntOfThreadWaiting--;
    byLastWaitorFound = (ptCond->bIsBroadcasting) &&
                        (0 == ptCond->nCntOfThreadWaiting);
    LeaveCriticalSection(&ptCond->lockForCntOfWaiting);

    if (byLastWaitorFound)
        SignalObjectAndWait(ptCond->hEventAllWakeUpDone,
                            ptExMutex->hMutex, INFINITE, FALSE);
    else
        WaitForSingleObject(ptExMutex->hMutex, INFINITE);

    ptExMutex->eState = MUTEX_STATE_LOCK;

    if (WAIT_OBJECT_0 == nRet)
        return 0;
    else if (WAIT_TIMEOUT == nRet)
        return -ETIMEDOUT;
    else if (WAIT_FAILED == nRet)
        return -ERR(GetLastError());
    else
        return -ERRNO_EWRONG;
}

EErrNo xIPCChkPthreadMutexValid(const pthread_mutex_t *ptMutex)
{
    if (!ptMutex)
        return -ERRNO_INVALID;

    return ((ptMutex->hMutex == NULL) ? ERRNO_INVALID : ERRNO_VALID);
}

typedef struct {
    pThreadFunc pCb;
    void       *pArg;
} TWinThreadData;

static DWORD __stdcall s_xIPC_START_ROUTINE(LPVOID lpThreadParameter)
{
    DWORD           dwRet = ERRNO_SUCCESS;
    TWinThreadData *ptThreadData = (TWinThreadData *)lpThreadParameter;

    if (ptThreadData && (ptThreadData->pCb))
    {
        dwRet = (DWORD)((ptThreadData->pCb)((void *)(ptThreadData->pArg)));
    }

    if (ptThreadData)
        free(ptThreadData);

    return dwRet;
}

int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
    pThreadFunc pCallback, void *arg)
{
    TWinThreadData *ptThreadData = NULL;

    if (!tidp || !pCallback)
        return -ERRNO_EPARAMETER_INVALID;

    ptThreadData = (TWinThreadData *)malloc(sizeof(TWinThreadData));
    if (!ptThreadData)
        return -ERRNO_EMALLOC_FAIL;

    ptThreadData->pCb  = pCallback;
    ptThreadData->pArg = arg;

    *(tidp) = CreateThread(NULL,
                           0,
                           (s_xIPC_START_ROUTINE),
                           ptThreadData,
                           0,
                           NULL);

    return ((NULL == *(tidp)) ? -FERR(1) : 0);
}


